package com.springboot.cloud.authentication.service.impl;

import com.springboot.cloud.authentication.provider.ResourceProvider;
import com.springboot.cloud.authentication.service.NewMvcRequestMatcher;
import com.springboot.cloud.authentication.service.ResourceService;
import com.springboot.cloud.core.model.SystemResource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>  </p>
 *
 * @author Bertram
 * @since 11/5/2019 2:13 PM
 */
@Service
@Slf4j
public class ResourceServiceImpl implements ResourceService {

    private HandlerMappingIntrospector handlerMappingIntrospector;
    private ResourceProvider resourceProvider;

    public ResourceServiceImpl(HandlerMappingIntrospector handlerMappingIntrospector, ResourceProvider resourceProvider) {
        this.handlerMappingIntrospector = handlerMappingIntrospector;
        this.resourceProvider = resourceProvider;
    }

    /**
     * 系统中所有权限集合
     */
    private Map<RequestMatcher, ConfigAttribute> resourceConfigAttributes;

    @Override
    public void saveResource(SystemResource systemResource) {
        resourceConfigAttributes.put(
                this.newMvcRequestMatcher(systemResource.getResourceUrl(), systemResource.getResourceMethod()),
                new SecurityConfig(systemResource.getResourceCode())
        );
        log.info("resourceConfigAttributes size:{}", this.resourceConfigAttributes.size());
    }

    @Override
    public void removeResource(SystemResource systemResource) {
        resourceConfigAttributes.remove(this.newMvcRequestMatcher(systemResource.getResourceUrl(), systemResource.getResourceMethod()));
        log.info("resourceConfigAttributes size:{}", this.resourceConfigAttributes.size());
    }

    @Override
    public Map<RequestMatcher, ConfigAttribute> loadResource() {
        Set<SystemResource> resources = resourceProvider.resources().getData();
        this.resourceConfigAttributes = resources.stream()
                .collect(Collectors.toMap(
                        resource -> this.newMvcRequestMatcher(resource.getResourceUrl(), resource.getResourceMethod()),
                        resource -> new SecurityConfig(resource.getResourceCode())
                ));
        log.debug("resourceConfigAttributes:{}", this.resourceConfigAttributes);
        return this.resourceConfigAttributes;
    }

    @Override
    public ConfigAttribute findConfigAttributesByUrl(HttpServletRequest authRequest) {
        return this.resourceConfigAttributes.keySet().stream()
                .filter(requestMatcher -> requestMatcher.matches(authRequest))
                .map(requestMatcher -> this.resourceConfigAttributes.get(requestMatcher))
                .peek(urlConfigAttribute -> log.debug("url在资源池中配置：{}", urlConfigAttribute.getAttribute()))
                .findFirst()
                .orElse(new SecurityConfig("NONEXISTENT_URL"));
    }

    @Override
    public Set<SystemResource> queryByUsername(String username) {
        return resourceProvider.resources(username).getData();
    }

    /**
     * 创建RequestMatcher
     *
     * @param url
     * @param method
     * @return
     */
    private MvcRequestMatcher newMvcRequestMatcher(String url, String method) {
        return new NewMvcRequestMatcher(handlerMappingIntrospector, url, method);
    }
}
